package com.yyaccp.jdbc.core;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class BeanPropertyRowMapper<T> implements RowMapper<T> {
    private Class<T> mappedClass;
    /**
     * key: 数据库小写的列名字，支持下划线；实体类属性名应该用标准的驼峰命名
     */
    private Map<String, PropertyDescriptor> mappedFields;

    public BeanPropertyRowMapper(Class<T> mappedClass) {
        initialize(mappedClass);
    }

    private void initialize(Class<T> mappedClass) {
        this.mappedClass = mappedClass;
        this.mappedFields = new HashMap<>();
        // 通过java bean内省获取属性
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(mappedClass);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        Arrays.stream(beanInfo.getPropertyDescriptors()).forEach(propertyDescriptor -> {
            if(propertyDescriptor.getWriteMethod() != null) {
                String name = propertyDescriptor.getName();
                String lowerCaseName = lowerCaseName(name);
                mappedFields.put(name, propertyDescriptor);
                // 假定列名是用下划线规则
                String underscoreName = underscoreName(name);
                if(!underscoreName.equals(lowerCaseName)) {
                    mappedFields.put(underscoreName, propertyDescriptor);
                }
            }
        });
    }

    @Override
    public T mapRow(ResultSet rs) throws SQLException {
        try {
            T mappedObject = mappedClass.newInstance();
            ResultSetMetaData resultSetMetaData = rs.getMetaData();
            for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                String columnName = resultSetMetaData.getColumnName(i);
                PropertyDescriptor propertyDescriptor = this.mappedFields.get(columnName.toLowerCase());
                if(propertyDescriptor != null) {
                    Object value = rs.getObject(i);
                    setValue(mappedObject, propertyDescriptor, value);
                }
            }
            return mappedObject;
        } catch (Exception e) {
            e.printStackTrace();
            throw new SQLException(e.getMessage());
        }
    }

    private void setValue(T mappedObject, PropertyDescriptor propertyDescriptor, Object value) throws IllegalAccessException, InvocationTargetException {
        if(value == null) {
            Class clz = propertyDescriptor.getPropertyType();
            if (clz == Integer.TYPE) value = 0;
            else if(clz == Long.TYPE) value = 0L;
            else if(clz == Float.TYPE) value = .0F;
            else if(clz == Double.TYPE) value = .0;
            else if(clz == Boolean.TYPE) value = false;
        }
        propertyDescriptor.getWriteMethod().invoke(mappedObject, value);
    }

    protected String underscoreName(String name) {
        if (name == null || name.trim().length() == 0) {
            return "";
        }

        StringBuilder result = new StringBuilder();
        result.append(lowerCaseName(name.substring(0, 1)));
        for (int i = 1; i < name.length(); i++) {
            String s = name.substring(i, i + 1);
            String slc = lowerCaseName(s);
            if (!s.equals(slc)) {
                result.append("_").append(slc);
            }
            else {
                result.append(s);
            }
        }
        return result.toString();
    }

    protected String lowerCaseName(String name) {
        return name.toLowerCase(Locale.US);
    }

}
